#include <BlockDropperUI.h>

#include <iostream>

#include <SDL/SDL.h>
#include <SDL/SDL_keyboard.h>

#include <FreeBlock.h>
#include <Rectangle.h>

namespace BD = BlockDropper;

namespace
{
	BD::Colour colourBlack = {  0,   0,   0, 255};
	BD::Colour colourGrey  = {128, 128, 128, 255};
	BD::Colour colourPink  = {255, 128, 128, 255};
	BD::Colour colourRed   = {255,   0,   0, 255};
	BD::Colour colourWhite = {255, 255, 255, 255};

	BD::Rectangle getBoardLocation(const BD::BlockDropperGame & game, 
	                           const BD::VideoOutput & videoOutput)
	{
		const double gameAspect(static_cast<double>(game.width) / game.height);
		const double screenAspect(static_cast<double>(videoOutput.width()) / videoOutput.height());

		BD::Rectangle rectangle;

		const double marginSize(0.1);

		if(gameAspect > screenAspect)
		{
			rectangle.topLeft.x = marginSize * videoOutput.width();
			rectangle.bottomRight.x = (1.0 - marginSize) * videoOutput.width();
			const double width(videoOutput.width() * (1.0 - marginSize * 2.0));
			const double height(width / gameAspect);
			rectangle.topLeft.y = (videoOutput.height() - height) / 2.0;
			rectangle.bottomRight.y = videoOutput.height() - rectangle.topLeft.y;
		}
		else
		{

			rectangle.topLeft.y = marginSize * videoOutput.height();
			rectangle.bottomRight.y = (1.0 - marginSize) * videoOutput.height();
			const double height(videoOutput.height() * (1.0 - marginSize * 2.0));
			const double width(height * gameAspect);
			rectangle.topLeft.x = (videoOutput.width() - width) / 2.0;
			rectangle.bottomRight.x = videoOutput.width() - rectangle.topLeft.x;
		}

		return rectangle;
	}
} // end namespace anonymous

BD::BlockDropperUI::BlockDropperUI(const BlockDropperGame & newGame, VideoOutput & newVideoOutput)
	: game(newGame)
	, videoOutput(newVideoOutput)
{
}

void BD::BlockDropperUI::drawBlock(const CellState cell, const Point<unsigned short> & position)
{
	const Rectangle board(getBoardLocation(game, videoOutput));
	const Rectangle rectangle(translatePoint(position), 
                           translatePoint(Point<unsigned short>(position.x + 1, position.y + 1)));

	videoOutput.drawRectangle(rectangle, cell.value ? (cell.markDeletion ? colourPink : colourRed) : (cell.markDeletion ? colourGrey : colourWhite));
}

template<typename T>
BD::Point<unsigned short> BD::BlockDropperUI::translatePoint(const BD::Point<T> & point)
{
	const Rectangle board(getBoardLocation(game, videoOutput));
	const double x(board.left() + point.x * board.width()  / game.width );
	const double y(board.top()  + point.y * board.height() / game.height);

	return Point<unsigned short>(x, y);
}

BD::BlockDropperActions BD::BlockDropperUI::update()
{
	const Rectangle board(getBoardLocation(game, videoOutput));

	videoOutput.drawRectangle(board, colourBlack);

	const FreeBlock & freeBlock(game.freeBlock());
	const Point<unsigned short> & position(freeBlock.position());
	drawBlock(freeBlock.getConstituentBlock(0, 0), Point<unsigned short>(position.x    , position.y    ));
	drawBlock(freeBlock.getConstituentBlock(0, 1), Point<unsigned short>(position.x    , position.y + 1));
	drawBlock(freeBlock.getConstituentBlock(1, 0), Point<unsigned short>(position.x + 1, position.y    ));
	drawBlock(freeBlock.getConstituentBlock(1, 1), Point<unsigned short>(position.x + 1, position.y + 1));

	for(unsigned short y(0); y <= game.height; ++y)
	{
		const Point<unsigned short> topLeft     = translatePoint(Point<unsigned short>(0         , y));
		const Point<unsigned short> bottomRight = translatePoint(Point<unsigned short>(game.width, y));

		videoOutput.drawLine(topLeft, bottomRight, colourGrey);
	}

	for(unsigned short x(0); x <= game.width; ++x)
	{
		const Point<unsigned short> topLeft     = translatePoint(Point<unsigned short>(x, 0          ));
		const Point<unsigned short> bottomRight = translatePoint(Point<unsigned short>(x, game.height));

		videoOutput.drawLine(topLeft, bottomRight, colourGrey);
	}

	for(unsigned short y(0); y < game.height; ++y)
	{
		for(unsigned short x(0); x < game.width; ++x)
		{
			const Point<unsigned short> point(x, y);
			const boost::optional<CellState> & boardItem(game.getBoardItem(point));

			if(boardItem)
			{
				drawBlock(*boardItem, point);
			}
		}
	}

	const Point<unsigned short> topLeft     = translatePoint(Point<double>(game.clearerPos(), 0          ));
	const Point<unsigned short> bottomRight = translatePoint(Point<double>(game.clearerPos(), game.height));
	videoOutput.drawLine(topLeft, bottomRight, colourWhite);

	videoOutput.update();

	SDL_PumpEvents();
	const unsigned char * keyStates(SDL_GetKeyState(nullptr));

	BlockDropperActions actions;

	if(keyStates[SDLK_LEFT ]) actions.insert(ACTION_MOVE_LEFT         );
	if(keyStates[SDLK_RIGHT]) actions.insert(ACTION_MOVE_RIGHT        );
	if(keyStates[SDLK_z    ]) actions.insert(ACTION_TURN_ANTICLOCKWISE);
	if(keyStates[SDLK_x    ]) actions.insert(ACTION_TURN_CLOCKWISE    );
	if(keyStates[SDLK_DOWN ]) actions.insert(ACTION_MOVE_DOWN         );

	return actions;
}

